//
//  DXFParser.m
//  dxfreader
//
//  Created by Holmes Futrell on 5/20/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "DXFParser.h"
#import "DXFNode.h"

#define ANY_CODE -1


NSString *trimLeading(NSString * src) {
    NSInteger i = 0;
    while (i<[src length] && [[NSCharacterSet whitespaceCharacterSet] characterIsMember:[src characterAtIndex:i]]) {
        i++;
    }
    return [src substringFromIndex:i];
}

@interface DXFParser ()
@property (readwrite, retain) DXFNode *rootEntity;
@end


@implementation DXFParser

@synthesize rootEntity;

static void strip( char *s ) {
	int len = strlen(s);
	int i;	
		
	for (i=0; i<=len; i++) {
		if (s[i] == '\n' || s[i] == '\r') s[i] = '\0';
	}
							 
}

-(DXFNode *)currentNode {
	return (DXFNode *)[nodeStack lastObject];
}
-(void)popNode {
	[nodeStack removeLastObject];
}
-(void)pushNode:(DXFNode *)node {
	[[self currentNode] addChild: node];
	[nodeStack addObject: node];
}

-(int)getNextRecord {
		
	char s1[255];
	char s2[255];
	int n = 255;
	
	if (fgets( s1, n, inputFile ) == NULL) {
		isEOF = YES;
		return 0;
	}
	if (fgets( s2, n, inputFile ) == NULL) {
		isEOF = YES;
		return 0;
	}
	
	strip(s1);
	strip(s2);
	
	currentGroupCode = [[NSString stringWithUTF8String: s1] intValue];
	
	if ( currentData ) [ currentData release];
	currentData		 = [NSString stringWithUTF8String: s2];
	currentData		 = trimLeading(currentData);
	currentData		 = [ currentData retain ];
	
	return 1;
	
	
}

-(BOOL)checkRecordWithGroupCode:(int)code data:(NSString *)data {
	if ( code != ANY_CODE && code != currentGroupCode ) {
		return NO;
	}
	if ( data && ![data isEqual: currentData] ) {
		return NO;
	}
	return YES;
}

-(void)eatRecordWithGroupCode:(int)code data:(NSString *)data {
	if (![self checkRecordWithGroupCode: code data: data]) {
		NSLog(@"Parse error: expected code=%d, data=%@", code, data);
		NSLog(@"Got code = %d, data=%@", currentGroupCode, currentData);
	}	
	[self getNextRecord];
}

-(void)parseProperty {
	
	DXFNode *node = [[ DXFNode alloc ] initWithGroupId: currentGroupCode data: currentData ];
	[self pushNode: node];
	
	[self eatRecordWithGroupCode: ANY_CODE data: NULL];
	
	[self popNode];
	[node release];
	
}

-(void)parseEntity {
		
	//NSLog(@"Parse entity");
	
	DXFNode *node = [[ DXFNode alloc ] initWithGroupId: currentGroupCode data: currentData ];
	[self pushNode: node];
	
	[self eatRecordWithGroupCode: 0 data: NULL];
		
	BOOL entitiesFollow = 0;
	
	while( 1 ) {
		
		if ( [self checkRecordWithGroupCode: 66 data: @"1" ] ) {
			entitiesFollow = 1;
		}

		if ( currentGroupCode == 0 ) {
			
			if ([currentData isEqual:@"SEQEND"]) {
				entitiesFollow = 0;
			}
			if ( entitiesFollow ) {
				[self parseEntity];
			}
			else {
				break;
			}
		}
		
		else {
			[self parseProperty];
		}
	}
	
	if ([node.stringValue isEqual:@"LAYER"]) {
		if ( [node childExistsForCode: 2] ) {
			//NSLog(@"Add layer %@", [[node childOfCode: 2] stringValue]);
			[layers setObject: node forKey: [[node childOfCode: 2] stringValue] ];
		}
	}
	if ([node.stringValue isEqual:@"VIEWPORT"]) {
		if ( [node childExistsForCode: 69] && [[ node childOfCode: 69 ] intValue] == 1) {
			// Viewport ID == 1 indicates paperspace viewport
			paperspaceViewport = node;
		}
	}
	
	[self popNode];
	
	
}

-(void)parseBlock {
		
	DXFNode *node = [[ DXFNode alloc ] initWithGroupId: currentGroupCode data: currentData ];
	[self pushNode: node];
	
	[self eatRecordWithGroupCode: 0 data: @"BLOCK"];
	
	while( 1 ) {
		
		if ( [self checkRecordWithGroupCode: 0 data: @"ENDBLK" ]) {
			[self eatRecordWithGroupCode: 0 data: @"ENDBLK"];
			break;
		}
		else if ( currentGroupCode == 0 ) {
			[self parseEntity];
		}
		else {
			[self parseProperty];
		}
		
	}
	
	[self popNode];
	
	NSString *name = [[ node childOfCode: 2 ] stringValue ];
	[ blocks setObject: node forKey: name ];
	
	if ( [node childExistsForCode: 10 ] &&  [[node childOfCode: 10] doubleValue ] != 0.0 ) {
		NSLog(@"Block has x origin set! = %f", [ [node childOfCode: 10] doubleValue ]);
	}	
	if ( [node childExistsForCode: 20 ] && [[node childOfCode: 20] doubleValue ] != 0.0 ) {
		NSLog(@"Block has y origin set! = %f", [ [node childOfCode: 20] doubleValue ]);
	}	
	
	[node release];
	
}

-(void)parseEntities {
	
	//NSLog(@"Parse Entities");
	
	[self eatRecordWithGroupCode: 2 data: @"ENTITIES" ];
		
	self.rootEntity = [self currentNode];
	
	while( 1 ) {
		if ( [self checkRecordWithGroupCode: 0 data: @"ENDSEC"] ) {
			[self eatRecordWithGroupCode: 0 data: @"ENDSEC"];
			break;
		}
		else {
			[self parseEntity];
		}
	}
		
}

-(void)parseBlocks {
	
	[self eatRecordWithGroupCode: 2 data: @"BLOCKS" ];
		
	while( 1 ) {
		if ( [self checkRecordWithGroupCode: 0 data:@"BLOCK"] ) {
			[self parseBlock];
		}
		else if ([self checkRecordWithGroupCode: 0 data:@"ENDSEC"]) {
			break;
		}
		else {
			[self eatRecordWithGroupCode: ANY_CODE data: NULL];
		}
	}
		
}

-(void)parseTable {
	
	
	
	DXFNode *node = [[DXFNode alloc] initWithGroupId: 0 data: currentData ];
	[self pushNode: node];
	
	[self eatRecordWithGroupCode: 0 data: @"TABLE" ];
	
	while( 1 ) {
		if ( [self checkRecordWithGroupCode: 0 data:@"ENDTAB"] ) {
			break;
		}
		else if ( [self checkRecordWithGroupCode: 0 data: NULL] ) {
			[self parseEntity];
		}
		else if ( [self checkRecordWithGroupCode: ANY_CODE data: NULL] ) {
			[self parseProperty];
		}
	}
	
	[self eatRecordWithGroupCode: 0 data: @"ENDTAB" ];

	if ( [node childExistsForCode: 2] ) {
		NSString *key = [[node childOfCode: 2] stringValue];
	//	NSLog(@"Add table %@", key);
		[ tables setObject: node forKey: key];
	}	
	
	[node release];
	[self popNode];

	
}


-(void)parseTables {

	[self eatRecordWithGroupCode: 2 data: @"TABLES" ];

	while( 1 ) {
		
		if ([self checkRecordWithGroupCode: 0 data:@"TABLE"]) {
			[self parseTable];
		}		
		else if ([self checkRecordWithGroupCode: 0 data:@"ENDSEC"]) {
			break;
		}
		else {
			[self eatRecordWithGroupCode: ANY_CODE data: NULL];
		}
		
		
	}
	
}


-(void)parseSection {
	
	//NSLog(@"Parse section");
	
	DXFNode *node = [[ DXFNode alloc ] initWithGroupId: currentGroupCode data: currentData ];
	[self pushNode: node];
	
	[self eatRecordWithGroupCode: 0 data: @"SECTION"];
	
	while( 1 ) {
		if ([self checkRecordWithGroupCode: 2 data:@"TABLES"]) {
			[self parseTables];
			break;
		}		
		if ([self checkRecordWithGroupCode: 2 data:@"BLOCKS"]) {
			[self parseBlocks];
			break;
		}
		else if ([self checkRecordWithGroupCode: 2 data:@"ENTITIES"]) {
			[self parseEntities];
			break;
		}
		else if ([self checkRecordWithGroupCode: 0 data:@"ENDSEC"]) {
			[self eatRecordWithGroupCode: 0 data: @"ENDSEC"];
			break;
		}
		else if ([self checkRecordWithGroupCode: 0 data:@"EOF"]) {
			break;
		}
		else if (isEOF) {
			break;
		}
		else {
			[self eatRecordWithGroupCode: ANY_CODE data: NULL];
		}
	}
	
	[node release];
	[self popNode];
	
}

-(id)init {

	self = [super init];
	
	blocks = [[NSMutableDictionary alloc] init];
	layers = [[NSMutableDictionary alloc] init];
	tables = [[NSMutableDictionary alloc] init];
	
	nodeStack = [[NSMutableArray alloc] init];
	paperspaceViewport = nil;
	
	self.rootEntity = nil;
	
	return self;
	
}

-(DXFNode *)paperspaceViewport {
	return paperspaceViewport;
}

-(DXFNode *)getFirstActiveViewPort {
	
	if ( tables ) {
		DXFNode *viewportTable = (DXFNode *)[tables objectForKey:@"VPORT" ];
		if (viewportTable) {
			for (DXFNode *node in viewportTable.children) {
				if ([node childExistsForCode: 2]) {
					if ([[[node childOfCode: 2] stringValue] isEqual:@"*ACTIVE"]) {
						return node;
					}
				}
			}
		}
	}
	return nil;
	
}

-(DXFNode *)getBlock:(NSString *)blockName {
	return [ blocks objectForKey: blockName];
}
-(DXFNode *)getLayer:(NSString *)layerName {
	return [ layers objectForKey: layerName];
}


-(void)parseFile:(NSString *)file {
	
	inputFile = fopen([file UTF8String], "r");
	
	isEOF = NO;
	
	[self getNextRecord];
	
	while( 1 ) {
		
		if ( isEOF ) {
			break;
		}			
		else if ([self checkRecordWithGroupCode: 0 data:@"SECTION"]) {
			[self parseSection];
		}
		else if ([self checkRecordWithGroupCode: 0 data:@"EOF"]) {
			//NSLog(@"EOF");
			[self eatRecordWithGroupCode: 0 data:@"EOF"];
			break;
		}
		else {
			[self eatRecordWithGroupCode: ANY_CODE data: NULL];
		}
		
	}
	
	//NSLog(@"blocks count = %d", [blocks count]);
	
}

@end
